﻿using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;

namespace BepuUtilities
{
    public static class Vector64
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal unsafe static ref T GetElementUnsafe<T>(ref this Vector64<T> vector, int index)
        {
            ref T source = ref Unsafe.As<Vector64<T>, T>(ref Unsafe.AsRef(ref vector));
            return ref Unsafe.Add(ref source, index);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal unsafe static void SetElementUnsafe<T>(ref this Vector64<T> vector, int index, T value)
        {
            ref T source = ref Unsafe.As<Vector64<T>, T>(ref Unsafe.AsRef(ref vector));
            Unsafe.Add(ref source, index) = value;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector64<T> Create<T>(T value)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            for (int i = 0; i < Vector64<T>.Count; i++)
            {
                result.SetElementUnsafe(i, value);
            }
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe static Vector64<int> Create(int e0, int e1)
        {
            int* intPtr = stackalloc int[2];
            *intPtr = e0;
            *(intPtr + 1) = e1;
            int* source = intPtr;
            return Unsafe.AsRef<Vector64<int>>(source);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe static Vector64<uint> Create(uint e0, uint e1)
        {
            int* intPtr = stackalloc int[2];
            *intPtr = (int)e0;
            *(intPtr + 1) = (int)e1;
            uint* source = (uint*)intPtr;
            return Unsafe.AsRef<Vector64<uint>>(source);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static uint ExtractMostSignificantBits<T>(this Vector64<T> vector)
        {
            uint num = 0U;
            for (int i = 0; i < Vector64<T>.Count; i++)
            {
                uint num2 = Scalar<T>.ExtractMostSignificantBit(vector.GetElementUnsafe(i));
                num |= num2 << i;
            }
            return num;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector64<T> LessThan<T>(Vector64<T> left, Vector64<T> right)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            for (int i = 0; i < Vector64<T>.Count; i++)
            {
                T value = Scalar<T>.LessThan(left.GetElementUnsafe(i), right.GetElementUnsafe(i)) ? Scalar<T>.AllBitsSet : default;
                result.SetElementUnsafe(i, value);
            }
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector64<T> LessThanOrEqual<T>(Vector64<T> left, Vector64<T> right)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            for (int i = 0; i < Vector64<T>.Count; i++)
            {
                T value = Scalar<T>.LessThanOrEqual(left.GetElementUnsafe(i), right.GetElementUnsafe(i)) ? Scalar<T>.AllBitsSet : default;
                result.SetElementUnsafe(i, value);
            }
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector64<T> Max<T>(Vector64<T> left, Vector64<T> right)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            for (int i = 0; i < Vector64<T>.Count; i++)
            {
                T value = Scalar<T>.GreaterThan(left.GetElementUnsafe(i), right.GetElementUnsafe(i)) ? left.GetElementUnsafe(i) : right.GetElementUnsafe(i);
                result.SetElementUnsafe(i, value);
            }
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector64<T> Min<T>(Vector64<T> left, Vector64<T> right)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            for (int i = 0; i < Vector64<T>.Count; i++)
            {
                T value = Scalar<T>.LessThan(left.GetElementUnsafe(i), right.GetElementUnsafe(i)) ? left.GetElementUnsafe(i) : right.GetElementUnsafe(i);
                result.SetElementUnsafe(i, value);
            }
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector64<T> Equals<T>(Vector64<T> left, Vector64<T> right)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            for (int i = 0; i < Vector64<T>.Count; i++)
            {
                T value = Scalar<T>.Equals(left.GetElementUnsafe(i), right.GetElementUnsafe(i)) ? Scalar<T>.AllBitsSet : default;
                result.SetElementUnsafe(i, value);
            }
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe static Vector64<T> ConditionalSelect<T>(Vector64<T> condition, Vector64<T> left, Vector64<T> right)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            Unsafe.AsRef(ref result._00) = (left._00 & condition._00) | (right._00 & ~condition._00);
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe static Vector64<T> AndNot<T>(Vector64<T> left, Vector64<T> right)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            Unsafe.AsRef(ref result._00) = left._00 & ~right._00;
            return result;
        }
    }

    [DebuggerTypeProxy(typeof(Vector64DebugView<>))]
    [StructLayout(LayoutKind.Sequential, Size = 8)]
    public struct Vector64<T>
    {
        internal ulong _00;

        public static Vector64<T> AllBitsSet
        {
            get
            {
                Unsafe.SkipInit(out Vector64<T> result);
                Unsafe.AsRef(ref result._00) = ulong.MaxValue;
                return result;
            }
        }

        public static int Count
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                return 2;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector64<T> operator +(Vector64<T> left, Vector64<T> right)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            for (int i = 0; i < Count; i++)
            {
                T value = Scalar<T>.Add(left.GetElementUnsafe(i), right.GetElementUnsafe(i));
                result.SetElementUnsafe(i, value);
            }
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe static Vector64<T> operator |(Vector64<T> left, Vector64<T> right)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            Unsafe.AsRef(ref result._00) = left._00 | right._00;
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector64<T> operator -(Vector64<T> left, Vector64<T> right)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            for (int i = 0; i < Count; i++)
            {
                T value = Scalar<T>.Subtract(left.GetElementUnsafe(i), right.GetElementUnsafe(i));
                result.SetElementUnsafe(i, value);
            }
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe static Vector64<T> operator &(Vector64<T> left, Vector64<T> right)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            Unsafe.AsRef(ref result._00) = left._00 & right._00;
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe static Vector64<T> operator ^(Vector64<T> left, Vector64<T> right)
        {
            Unsafe.SkipInit(out Vector64<T> result);
            Unsafe.AsRef(ref result._00) = left._00 ^ right._00;
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public override string ToString()
        {
            return ToString("G", CultureInfo.InvariantCulture);
        }

        private unsafe string ToString([StringSyntax("NumericFormat")] string format, IFormatProvider formatProvider)
        {
            var valueStringBuilder = new StringBuilder();
            string numberGroupSeparator = NumberFormatInfo.GetInstance(formatProvider).NumberGroupSeparator;
            valueStringBuilder.Append('<');
            valueStringBuilder.Append(((IFormattable)(object)this.GetElementUnsafe(0)).ToString(format, formatProvider));
            for (int i = 1; i < Count; i++)
            {
                valueStringBuilder.Append(numberGroupSeparator);
                valueStringBuilder.Append(' ');
                valueStringBuilder.Append(((IFormattable)(object)this.GetElementUnsafe(i)).ToString(format, formatProvider));
            }
            valueStringBuilder.Append('>');
            return valueStringBuilder.ToString();
        }
    }
}
